package moe.dao;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import moe.dao.pool.ConnectionManager;
import moe.entity.Tag;
import moe.util.Paging;
import moe.util.QueryBuilder;

public class TagDao extends BaseDao {
	private static final String TAG_TIPS = "SELECT * FROM `tags` WHERE `name` like ? ESCAPE ? ORDER BY `count` DESC LIMIT 0, ?";
	private static final String LIST_TAGS = "SELECT * FROM `tags` LIMIT ?, ?";

	private static final String ESCAPE_CHAR = ConnectionManager.getDefaultManager()
			.getSearchStringEscape();

	private static class Tips {
		public String label;
		public int count;
		public int type;

		public Tips(String label, int count, int type) {
			this.label = label;
			this.count = count;
			this.type = type;
		}
	}

	public TagDao() {
		super(BaseDao.ConnType.DEFAULT);
	}

	public TagDao(ConnType connType) {
		super(connType);
	}

	public List<Tag> tagInfo(String[] tags) {
		QueryBuilder builder = new QueryBuilder("tags", null);
		int length = tags.length;
		Object[] args = new Object[length];
		for (int i = 0; i < length; i++) {
			args[i] = QueryBuilder.PLACEHOLDER;
		}
		builder.in("name", args);
		return this.query(builder.build(), false, new ResultsetHandler<List<Tag>>() {
			public List<Tag> handle(ResultSet rs) throws SQLException {
				List<Tag> result = new LinkedList<Tag>();
				while (rs.next()) {
					Tag bean = new Tag();
					result.add(bean);
					AnnotationColumnMapper.resultSetMap(rs, bean);
				}
				return result;
			}
		}, tags);
	}

	public List<Tag> queryTags(String sql) {
		return this.query(sql, new ResultsetHandler<List<Tag>>() {
			public List<Tag> handle(ResultSet rs) throws SQLException {
				List<Tag> result = new LinkedList<Tag>();
				while (rs.next()) {
					Tag bean = new Tag();
					result.add(bean);
					AnnotationColumnMapper.resultSetMap(rs, bean);
				}
				return result;
			}
		});
	}

	public List getTagTips(String tag, int number) {
		PreparedStatement stat = null;
		List<Tips> result = new LinkedList<Tips>();
		tag = tag.replace(ESCAPE_CHAR, ESCAPE_CHAR + ESCAPE_CHAR)
				.replace("%", ESCAPE_CHAR + "%")
				.replace("_", ESCAPE_CHAR + "_");
		try {
			stat = prepareStatement(TAG_TIPS);
			stat.setString(1, tag + "%");
			stat.setString(2, ESCAPE_CHAR);
			stat.setInt(3, number);
			ResultSet rs = stat.executeQuery();
			while (rs.next()) {
				result.add(new Tips(rs.getString("name"), rs.getInt("count"), rs.getInt("type")));
			}
			rs.close();
			if (result.size() < number) {
				stat.setString(1, "%" + tag + "%");
				stat.setInt(3, number);
				rs = stat.executeQuery();

				RS: while (rs.next() && result.size() < number) {
					for (Tips t : result) {
						if (t.label.equals(rs.getString("name"))) {
							continue RS;
						}
					}
					result.add(new Tips(rs.getString("name"), rs.getInt("count"), rs.getInt("type")));
				}
			}
			rs.close();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			close(stat);
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	public List<Tag> listTags(final Paging paging) {
		String sql = LIST_TAGS;
		if (!evaluateTotalCount(paging, sql)) {
			return Collections.EMPTY_LIST;
		}
		return this.query(sql, new ResultsetHandler<List<Tag>>() {
			public List<Tag> handle(ResultSet rs) throws SQLException {
				List<Tag> result = new ArrayList<Tag>(paging.getPageSize());
				Tag tag = null;
				while (rs.next()) {
					tag = new Tag();
					result.add(tag);
					AnnotationColumnMapper.resultSetMap(rs, tag);
				}
				return result;
			}
		}, paging.getPagingItemOffset(), paging.getPageSize());
	}

	public void insertTags(List<Tag> list) {
		List<String> sqls = new ArrayList<String>(list.size());
		for (Tag tag : list) {
			sqls.add(AnnotationColumnMapper.mappingToInsert("tags", tag));
		}
		this.executeBatch(sqls);
	}
}
